
package net.qiqbframework.messaging.annotation;


import net.qiqbframework.common.sorting.AnnotatedPriorityComparator;

import java.lang.reflect.Executable;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;


public class MultiParameterResolverFactory implements HandlerParameterResolverFactory {

    private final HandlerParameterResolverFactory[] factories;

    public static MultiParameterResolverFactory ordered(HandlerParameterResolverFactory... delegates) {
        return ordered(Arrays.asList(delegates));
    }


    public static MultiParameterResolverFactory ordered(List<HandlerParameterResolverFactory> delegates) {
        return new MultiParameterResolverFactory(flatten(delegates));
    }

    public MultiParameterResolverFactory(HandlerParameterResolverFactory... delegates) {
        this.factories = Arrays.copyOf(delegates, delegates.length);
    }

    public MultiParameterResolverFactory(List<HandlerParameterResolverFactory> delegates) {
        this.factories = delegates.toArray(new HandlerParameterResolverFactory[0]);
    }

    private static HandlerParameterResolverFactory[] flatten(List<HandlerParameterResolverFactory> factories) {
        List<HandlerParameterResolverFactory> flattened = new ArrayList<>(factories.size());
        for (HandlerParameterResolverFactory parameterResolverFactory : factories) {
            if (parameterResolverFactory instanceof MultiParameterResolverFactory) {
                flattened.addAll(((MultiParameterResolverFactory) parameterResolverFactory).getDelegates());
            } else {
                flattened.add(parameterResolverFactory);
            }
        }
        flattened.sort(AnnotatedPriorityComparator.getInstance());
        return flattened.toArray(new HandlerParameterResolverFactory[0]);
    }


    public List<HandlerParameterResolverFactory> getDelegates() {
        return Arrays.asList(factories);
    }


    @Override
    public HandlerParameterResolver createInstance(Executable executable, Parameter[] parameters, int parameterIndex) {
        for (HandlerParameterResolverFactory factory : factories) {
            HandlerParameterResolver resolver = factory.createInstance(executable, parameters, parameterIndex);
            if (resolver != null) {
                return resolver;
            }
        }
        return null;
    }
}
